﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Threading;
using System.Windows.Markup;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Reflection;

using Invini.Tools;

namespace Invini.Training
{
    public class RestrictTextAction : TrainingActionBase
    {
        private class ActiveBinding
        {
            private BindingProxy proxy;
            private PropertyChangedCallback handler;

            public ActiveBinding(BindingProxy proxy, PropertyChangedCallback handler)
            {
                this.proxy = proxy;
                this.handler = handler;
            }
            public void Clear()
            {
                this.proxy.ValueChanged -= this.handler;
                BindingOperations.ClearAllBindings(this.proxy);
            }
        }

        public PathComponentsCollection Element
        {
            get;
            set;
        }
        public string PropertyName
        {
            get;
            set;
        }
        public string Text
        {
            get;
            set;
        }

        private List<ActiveBinding> activeBindings;

        public RestrictTextAction()
        {
            this.Element = new PathComponentsCollection();
        }
        public override void Execute()
        {
            if (this.Element == null)
            {
                this.onCompleted();
                return;
            }

            IEnumerable<object> objects = this.Element.FindElements();
            if (objects.Count() == 0)
            {
                this.onCompleted();
                return;
            }

            this.activeBindings = new List<ActiveBinding>();

            foreach (object obj in objects)
            {
                DependencyObject dpo = obj as DependencyObject;
                if (dpo == null)
                    continue;

                Type type = dpo.GetType();
                FieldInfo dependencyPropertyInfo = type.GetField(String.Concat(this.PropertyName, "Property"), BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy);
                if (dependencyPropertyInfo == null)
                    continue;

                if (dependencyPropertyInfo.FieldType != typeof(DependencyProperty))
                    continue;

                DependencyProperty dependencyProperty = dependencyPropertyInfo.GetValue(null) as DependencyProperty;
                if (dependencyProperty == null)
                    continue;

                PropertyChangedCallback handler = new PropertyChangedCallback(this.proxy_ValueChanged);
                BindingProxy proxy = new BindingProxy();

                Binding binding = new Binding(this.PropertyName);
                binding.Source = dpo;
                binding.Mode = BindingMode.TwoWay;

                proxy.SetBinding(BindingProxy.FromProperty, binding);
                proxy.ValueChanged += handler;

                this.activeBindings.Add(new ActiveBinding(proxy, handler));
            }

            this.onCompleted();
        }
        public override void Clear()
        {
            base.Clear();

            if (this.activeBindings != null)
            {
                foreach (ActiveBinding activeBinding in this.activeBindings)
                    activeBinding.Clear();

                this.activeBindings = null;
            }
        }

        private void proxy_ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue == null || this.Text == null)
                return;

            if(!(e.NewValue is string))
                return;

            string text = e.NewValue as string;

            if (!this.Text.StartsWith(text))
                Application.Current.Dispatcher.BeginInvoke((Action)(() => d.SetValue(e.Property, e.OldValue)));
        }
    }
}
